package com.ningju.procurement.common.mybatis.pager;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;


@Intercepts({@Signature(
	type = StatementHandler.class,
	method = "prepare",
	args = {Connection.class, Integer.class}
)})
public class PagePlugin implements Interceptor {
	
	//Logger LOG = LoggerFactory.getLogger(PagePlugin.class);
	
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		
		Object target = invocation.getTarget();
		Object[] args = invocation.getArgs();
		PageParam pageParam = null;
		
		RoutingStatementHandler handler = (RoutingStatementHandler) target;
		BaseStatementHandler statementHandler = (BaseStatementHandler) ReflectUtils.getFieldValue(handler, "delegate");
		Object parameterObject = statementHandler.getParameterHandler().getParameterObject();
		BoundSql boundSql = statementHandler.getBoundSql();
		
		//是否存在分页语句，存在就不继续处理
		if(StringUtils.contains(boundSql.getSql().toLowerCase(), "limit")) {
			return invocation.proceed();
		}
		
		//获取PageParam对象
		pageParam = getPageParam(parameterObject);
		
		//自动处理分页
		if(null != pageParam) {
			MappedStatement mappedStatement = (MappedStatement)ReflectUtils.getFieldValue(BaseStatementHandler.class, statementHandler, "mappedStatement");
			Connection connection = (Connection) args[0];
			
			//得到总共记录数
			int totalRowCount = getTotalRowCount(boundSql, parameterObject, mappedStatement, connection);
			pageParam.setCount(totalRowCount);
			
			String newSql = getLimitSql(boundSql.getSql(), pageParam);
			if(StringUtils.isEmpty(newSql)) {
				return invocation.proceed();
			}
			ReflectUtils.setFieldValue(boundSql, "sql", newSql);
			PageContext.setPage(pageParam);//设置分页对象至上下文
		}
		
		return invocation.proceed();
	}
	
	@Override
	public Object plugin(Object target) {
		return target instanceof StatementHandler ? Plugin.wrap(target, this) : target;
	}

	@Override
	public void setProperties(Properties properties) {
		
	}
	
	/**
	 * 返回总记录数
	 * @param boundSql
	 * @param parameterObject
	 * @param mappedStatement
	 * @param connection
	 * @return
	 */
	private int getTotalRowCount(BoundSql boundSql, Object parameterObject, MappedStatement mappedStatement, Connection connection) {
		String sql = boundSql.getSql();
		String countSql = getCountSql(sql);
		
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, parameterObject);
		ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBoundSql);
		
		PreparedStatement pstmt = null;  
		ResultSet rs = null;
		int totalRecord = 0;
		
		try {
		   pstmt = connection.prepareStatement(countSql);
		   parameterHandler.setParameters(pstmt);
		   rs = pstmt.executeQuery();
		   if (rs.next()) {
		      totalRecord = rs.getInt(1);
		   }
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (pstmt != null)
					pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		return totalRecord;
	}
	
	/**
	 * 获取参数中的PageParam
	 * @param parameterObject
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private PageParam getPageParam(Object parameterObject) {
		PageParam pageParam = null;
		if(parameterObject instanceof Map) {
			Map<String, Object> params = (Map<String, Object>) parameterObject;
			for(String key : params.keySet()) {
				Object param = params.get(key);
				if(param instanceof PageParam) {
					pageParam = (PageParam) param;
					break;
				}
			}
		} else if(parameterObject instanceof PageParam) {
			pageParam = (PageParam) parameterObject;
		}
		return pageParam;
	}
	
	/**
	 * 获取分页sql
	 * @param rawSql
	 * @param pageParam 
	 * @return
	 */
	private String getLimitSql(String rawSql, PageParam pageParam) {
		if(StringUtils.isNotEmpty(rawSql)) {
			rawSql = rawSql + String.format(" LIMIT %d, %d", pageParam.getStart(), pageParam.getPageSize());
		}
		return rawSql;
	}
	
	/**
	 * 获取查询总记录数sql
	 * @param rawSql
	 * @return
	 */
	private String getCountSql(String rawSql) {
		if(StringUtils.isNotEmpty(rawSql)) {
			rawSql = "SELECT count(1) FROM (" + rawSql + ") as pc_t";
		}
		return rawSql;
	}

}
